#include "Block.h"
#include "Utility.h"
#include <sstream>
#include <cstring>

AbstractBlock::AbstractBlock()
: _size(0)
, _data(nullptr)
{}

AbstractBlock::AbstractBlock(unsigned int size)
: _size(size)
, _data(new unsigned char[_size]())
{
}

AbstractBlock::AbstractBlock(unsigned int size, unsigned char * data)
: AbstractBlock(size)
{
    memcpy(_data, data, size);
}


AbstractBlock::~AbstractBlock() {
    if (_data) {
        delete [] _data;
        _data = nullptr;
    }
    _size = 0;
}

unsigned int AbstractBlock::size() const {
    return _size;
}

unsigned char * AbstractBlock::data() const {
    return _data;
}

std::string AbstractBlock::toString() const {
    std::stringstream ss;
    ss << "AbstractBlock=>size:[" << size() << "], data:[" << (void *)data() << "]:";
    return ss.str();
}


static const int DEFAULT_CAPACITY_BASE = 4096;

Block::Block(unsigned int size)
: AbstractBlock(checkedAlign(size))
{
}

Block::Block(unsigned int size, unsigned char * data)
: AbstractBlock(size, data)
{
    _size = checkedAlign(size);
}

Block::Block(unsigned char c, unsigned int capacity, unsigned int repeat)
: AbstractBlock(checkedAlign(capacity))
{
    unsigned int size = this->size();
    if (repeat > size) {
        repeat = size;
    }

    unsigned char * data = this->data();
    for(unsigned int i = 0; i < repeat; ++i) {
        data[i] = c;
    }
}

Block::Block(Block && other) 
: AbstractBlock()
{
    _size = other._size;
    _data = other._data;

    other._data = nullptr;
    other._size = 0;
}

Block::Block(const Block & other)
: AbstractBlock(other._size)
{
    memcpy(_data, other._data, _size);
}

Block& Block::operator=(const Block & other) {
    if (this != &other) {
        if (_data) {
            delete [] _data;
        }

        _size = other._size;
        _data = new unsigned char[_size]();
        memcpy(_data, other._data, _size);
    }
    return *this;
}

Block::~Block() {
}

std::string Block::toString() const {
    std::stringstream ss;
    ss << "Block=>size:[" << size() << "], data:[" << (void *)data() << "]:";
    return ss.str();
}

unsigned int Block::checkedAlign(unsigned int size) {
    return BitUtility::alignGreaterEqual(size, DEFAULT_CAPACITY_BASE);
}

Blocks::Blocks(unsigned int blockSize) {
    _blocks.reserve(16);
    _blockSize = blockSize;
}

bool Blocks::append(Block & block) {
    if (_blockSize == block.size()) {
        //_blocks.push_back(std::move(block));
        _blocks.push_back(block);
        return true;
    }
    return false;
}

std::unique_ptr<Block> Blocks::remove(unsigned int index) {
    if (index < _blockSize) {
        auto block = std::make_unique<Block>(std::move(_blocks[index]));
        _blocks.erase(_blocks.begin() + index);
        return block;
    } else {
        return nullptr;
    }
}

std::shared_ptr<Block> Blocks::get(unsigned int index) {
    if (index < _blockSize) {
        return std::make_shared<Block>(_blocks[index].size(), _blocks[index].data());
    } else {
        return nullptr;
    }
}

void Blocks::clear() {
    _blocks.clear();
}

